package com.evil.application.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserService;
import com.evil.application.api.RemoteApplicationOrderNodeService;
import com.evil.application.enums.order.OrderQueryTypeEnum;
import com.evil.application.mapper.*;
import com.evil.application.pojo.dto.form.control.code.ControlDTO;
import com.evil.application.pojo.dto.order.OrderMyNodesReqDTO;
import com.evil.application.pojo.dto.order.log.OrderNodeAssigneeDTO;
import com.evil.application.pojo.dto.order.node.AddOrderNodeBaseReqDTO;
import com.evil.application.pojo.dto.order.node.HandleOrderNodeReqDTO;
import com.evil.application.pojo.dto.order.node.ModifyOrderNodeDataReqDTO;
import com.evil.application.pojo.dto.order.node.handler.QueryOrderNodeHandler;
import com.evil.application.pojo.entity.*;
import com.evil.application.service.*;
import com.evil.application.util.ReTriggerUtil;
import com.evil.common.account.dto.user.UserBaseRespDTO;
import com.evil.common.account.dto.user.UserEnterpriseBaseDTO;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.activiti.enums.HandleTypeEnum;
import com.evil.common.activiti.enums.NodeTypeEnum;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.dto.order.data.ControlDataValueDTO;
import com.evil.common.application.dto.order.data.NodeOtherInfoDTO;
import com.evil.common.application.dto.order.node.ChangeOrderNodeDataReqDTO;
import com.evil.common.application.dto.process.node.NodeBaseDTO;
import com.evil.common.application.enums.ApplicationVersionEnum;
import com.evil.common.application.enums.order.OrderProcessStatusEnum;
import com.evil.common.core.enums.BaseEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.function.Callback;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.family.dto.FindByIdsReqDTO;
import com.evil.common.family.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 应用订单节点记录表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationOrderNodeServiceImpl extends ServiceImpl<ApplicationOrderNodeMapper, ApplicationOrderNode>
        implements ApplicationOrderNodeService, RemoteApplicationOrderNodeService {

    private final LoginUtil loginUtil;

    private final ReTriggerUtil reTriggerUtil;

    private final ApplicationInfoMapper applicationInfoMapper;

    private final ApplicationFormMapper applicationFormMapper;

    private final ApplicationOrderMapper applicationOrderMapper;

    private final ApplicationProcessMapper applicationProcessMapper;

    private final ApplicationProcessNodeMapper applicationProcessNodeMapper;

    private final ApplicationFormControlService applicationFormControlService;

    private final ApplicationOrderDataService applicationOrderDataService;

    private final ApplicationOrderNodeHandlerService applicationOrderNodeHandlerService;

    private final ElasticsearchService elasticsearchService;

    private final RemoteUserService remoteUserService;

    private final ApplicationLogServiceImpl applicationLogService;

    /**
     * 订单我的节点列表
     *
     * @param orderMyNodesReqDTO orderMyNodesReqDTO
     * @return List
     */
    @Override
    public List<NodeBaseDTO> orderMyNodes(OrderMyNodesReqDTO orderMyNodesReqDTO) {
        Long loginUserId = loginUtil.getLoginUserId();
        Long orderId = orderMyNodesReqDTO.getOrderId();
        Integer orderQueryType = orderMyNodesReqDTO.getQueryType();

        applicationOrderMapper.isExistById(orderId);

        OrderQueryTypeEnum orderQueryTypeEnum = OrderQueryTypeEnum.findById(orderQueryType);

        // 获取处理类型列表
        List<Integer> handleTypes = HandleTypeEnum.findByClassify(orderQueryTypeEnum.getHandleTypeClassifyEnum());
        QueryOrderNodeHandler queryOrderNodeHandler = new QueryOrderNodeHandler(orderId, loginUserId, handleTypes);
        List<ApplicationOrderNodeHandler> nodeHandlers = applicationOrderNodeHandlerService.orderNodeHandlers(queryOrderNodeHandler);

        // 获取节点信息
        Set<Long> nodeIds = StreamUtil.transSetT(nodeHandlers, ApplicationOrderNodeHandler::getNodeId);

        // 过滤开始节点
        return applicationProcessNodeMapper.findByIds(nodeIds, false).stream()
                .filter(e -> NodeTypeEnum.START_EVENT.getId() != e.getNodeType())
                .map(e -> new NodeBaseDTO(e.getNodeId(), e.getNodeType(), e.getNodeName()))
                .collect(Collectors.toList());
    }

    /**
     * 新增订单节点
     *
     * @param addOrderNodeBaseReqDTO orderNodeBaseReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void addOrderNode(AddOrderNodeBaseReqDTO addOrderNodeBaseReqDTO) {
        Long orderId = addOrderNodeBaseReqDTO.getOrderId();
        Long nodeId = addOrderNodeBaseReqDTO.getNodeId();
        Integer handleType = addOrderNodeBaseReqDTO.getHandleType();
        Map<String, List<UserEnterpriseBaseDTO>> groupAssignees = addOrderNodeBaseReqDTO.getGroupAssignees();

        HandleTypeEnum.findById(handleType);
        // 根据流程实例获取订单
        ApplicationOrder applicationOrder = applicationOrderMapper.findById(orderId);
        // 检查流程节点是否存在（包括已删除的）
        applicationProcessNodeMapper.findById(nodeId, false);

        // 所有处理人用户ids
        List<Long> userIds = new ArrayList<>();
        groupAssignees.values().forEach(list -> {
            if (StreamUtil.anyMatch(list, e -> !applicationOrder.getEnterpriseId().equals(e.getEnterpriseId()))) {
                // 保证都是当前订单归属企业
                throw new BusinessException(RCodeEnum.ACTIVITY_PROCESS_NODE_HANDLERS_ERROR);
            }
            userIds.addAll(StreamUtil.transListT(list, UserEnterpriseBaseDTO::getUserId));
        });
        Map<Long, UserBaseRespDTO> userMap = remoteUserService.findBaseMapByIds(FindByIdsReqDTO.filter(userIds));

        // 该节点全部处理人
        List<OrderNodeAssigneeDTO> orderNodeAssignees = new ArrayList<>();
        groupAssignees.forEach((group, assignees) -> assignees.forEach(e -> {
            if (userMap.containsKey(e.getUserId())) {
                UserBaseRespDTO user = userMap.get(e.getUserId());
                orderNodeAssignees.add(new OrderNodeAssigneeDTO(user.getUserId(), user.getNickName(), group));
            }
        }));

        // 修改订单节点处理人
        applicationOrderNodeHandlerService.modifyOrderNodeHandler(orderId, nodeId, handleType, orderNodeAssignees);

        // 处理订单节点 （一个订单一个节点 一条记录）
        ApplicationOrderNode applicationOrderNode = this.baseMapper.findByOrderIdAndNodeId(orderId, nodeId).orElseGet(() -> {
            ApplicationOrderNode orderNode = new ApplicationOrderNode();
            orderNode.setOrderId(applicationOrder.getOrderId());
            orderNode.setNodeId(nodeId);
            return orderNode;
        });
        applicationOrderNode.setHandleType(handleType);
        applicationOrderNode.setHandleDate(new Date());
        applicationOrderNode.setAssignee(JSON.toJSONString(orderNodeAssignees));
        applicationOrderNode.setIsDeleted(SwitchEnum.NO.getId());
        applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrderNode::getOrderNodeId, applicationOrderNode);

        // 更新ES订单节点处理人
        applicationOrderNodeHandlerService.refreshEsOrderNodeHandler(applicationOrder);
    }

    /**
     * 订单节点流转
     *
     * @param handleOrderNodeReqDTO completeOrderNodeReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void handleOrderNode(HandleOrderNodeReqDTO handleOrderNodeReqDTO) {
        Long orderId = handleOrderNodeReqDTO.getOrderId();
        Long nodeId = handleOrderNodeReqDTO.getNodeId();
        Integer handleType = handleOrderNodeReqDTO.getHandleType();
        boolean isStart = HandleTypeEnum.START.getId() == handleType;

        HandleTypeEnum.findById(handleType);
        ApplicationOrder applicationOrder = applicationOrderMapper.findById(orderId);
        ApplicationProcessNode node = applicationProcessNodeMapper.findById(nodeId, false);

        // 修改订单节点处理人 -- 如果全部已处理，则返回处理时间
        Date handleDate = applicationOrderNodeHandlerService.handleOrderNodeHandler(handleOrderNodeReqDTO);

        // 如果是发起新的，则需要移除订单节点数据
        if (isStart) {
            this.changeOrderNodeData(new ChangeOrderNodeDataReqDTO(orderId, ListUtil.toList(nodeId), handleType));
        }

        // 是否全部已处理
        if (null != handleDate) {
            // 获取订单节点信息
            ApplicationOrderNode applicationOrderNode = this.baseMapper.findByOrderIdAndNodeId(orderId, nodeId)
                    .orElseThrow(() -> new BusinessException(BaseEnum.ERROR_PARAMETER));
            applicationOrderNode.setHandleType(handleType);
            applicationOrderNode.setHandleDate(handleDate);
            applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrderNode::getOrderNodeId, applicationOrderNode);
        }

        // 提交/通过时重新触发数据关联或公式
        if (HandleTypeEnum.isPass(handleType)) {
            // 有开启重新运算公式
            if (SwitchEnum.isOpen(node.getRecalculation())) {
                ApplicationInfo applicationInfo = applicationInfoMapper.findById(applicationOrder.getApplicationId());
                ApplicationForm applicationForm = applicationFormMapper.findByAppIdAndVersion(
                        applicationOrder.getApplicationId(), ApplicationVersionEnum.RELEASE_VERSION.getId());

                Map<Long, ControlDTO> controlMap = applicationFormControlService.appReleaseControlDTOMap_Cache(applicationInfo);

                List<ControlDataDTO> controlDataList = elasticsearchService.orderDataList(applicationOrder.getOrderId());
                Map<Long, ControlDataDTO> controlDataMap = StreamUtil.toMapK(controlDataList, ControlDataDTO::getControlId);
                List<Long> recalculationControlIds = JSON.parseArray(node.getRecalculationControlIds(), Long.class);
                // 重新运算公式
                reTriggerUtil.reTrigger(controlMap, controlDataMap, recalculationControlIds);

                // 保存订单数据
                applicationOrderDataService.handleAndSave(applicationInfo, applicationForm, applicationOrder, controlDataMap.values(), false);
            }
        }
        // 更新ES订单节点
        applicationOrderNodeHandlerService.refreshEsOrderNodeHandler(applicationOrder);
    }

    /**
     * 订单节点拒绝
     *
     * @param handleOrderNodeReqDTO completeOrderNodeReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void refuseOrderNode(HandleOrderNodeReqDTO handleOrderNodeReqDTO) {
        Long orderId = handleOrderNodeReqDTO.getOrderId();
        Long nodeId = handleOrderNodeReqDTO.getNodeId();

        applicationOrderMapper.findById(orderId);
        applicationProcessNodeMapper.findById(nodeId, false);

        // 处理订单节点处理人
        applicationOrderNodeHandlerService.handleOrderNodeHandler(handleOrderNodeReqDTO);

        // 处理订单数据
        ModifyOrderNodeDataReqDTO modifyOrderNodeDataReqDTO = new ModifyOrderNodeDataReqDTO();
        modifyOrderNodeDataReqDTO.setOrderId(orderId);
        modifyOrderNodeDataReqDTO.setIsCover(true);
        modifyOrderNodeDataReqDTO.setHandleType(HandleTypeEnum.REJECTED.getId());
        modifyOrderNodeDataReqDTO.setNodeHandlersMap(MapUtil.of(nodeId, new ArrayList<>()));
        this.modifyOrderNodeData(modifyOrderNodeDataReqDTO);
    }

    /**
     * 订单更新 当前节点控件 数据
     *
     * @param modifyOrderNodeDataReqDTO modifyOrderNodeDataReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void modifyOrderNodeData(ModifyOrderNodeDataReqDTO modifyOrderNodeDataReqDTO) {
        Long orderId = modifyOrderNodeDataReqDTO.getOrderId();
        Boolean isCover = modifyOrderNodeDataReqDTO.getIsCover();
        Boolean isOnlyModifyHandlers = modifyOrderNodeDataReqDTO.getIsOnlyModifyHandlers();
        Integer handleType = modifyOrderNodeDataReqDTO.getHandleType();
        Long parentNodeId = modifyOrderNodeDataReqDTO.getParentNodeId();
        Map<Long, List<UserEnterpriseBaseDTO>> nodeHandlersMap = modifyOrderNodeDataReqDTO.getNodeHandlersMap();

        HandleTypeEnum handleTypeEnum = HandleTypeEnum.findById(handleType);

        ApplicationOrder order = applicationOrderMapper.findById(orderId);

        // 当前节点
        List<ApplicationProcessNode> nodes = applicationProcessNodeMapper.findByIds(nodeHandlersMap.keySet(), false);
        if (nodeHandlersMap.keySet().size() != nodes.size()) {
            throw new BusinessException(RCodeEnum.APPLICATION_PROCESS_NODE_NOT_EXIST);
        }

        Callback endNode = () -> {
            ApplicationInfo applicationInfo = applicationInfoMapper.findById(order.getApplicationId());
            ApplicationProcess applicationProcess = applicationProcessMapper.findByAppIdAndVersion(
                    applicationInfo.getApplicationId(), ApplicationVersionEnum.RELEASE_VERSION.getId());
            ApplicationProcessNode node = applicationProcessNodeMapper.findEndNodeByProcessId(applicationProcess.getProcessId());
            nodeHandlersMap.clear();
            nodeHandlersMap.put(node.getNodeId(), new ArrayList<>());
        };

        Integer orderProcessStatus = null;
        if (HandleTypeEnum.REJECTED == handleTypeEnum) {
            // 审批拒绝
            endNode.apply();
            // 结束节点 -> 已拒绝
            orderProcessStatus = OrderProcessStatusEnum.REFUSE.getId();
        } else if (StreamUtil.anyMatch(nodes, e -> NodeTypeEnum.END_EVENT.getId() == e.getNodeType())) {
            // 结束节点
            if (HandleTypeEnum.APPROVED == handleTypeEnum) {
                endNode.apply();
                // 结束节点 -> 流程完成
                orderProcessStatus = OrderProcessStatusEnum.COMPLETED.getId();
            }
        }

        // 更新订单信息
        if (null != orderProcessStatus) {
            order.setProcessStatus(orderProcessStatus);

            List<ApplicationOrderNode> processingNodes = this.baseMapper.findProcessingOrderNode(orderId, HandleTypeEnum.PROCESSING.getId());
            processingNodes.forEach(n -> n.setHandleType(handleType));
            applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrderNode::getOrderNodeId, processingNodes);
        }

        // 更新订单节点控件数据
        applicationOrderDataService.modifyOrderNodeData(order, orderData -> {
            Map<Long, NodeBaseDTO> nodeMap = applicationProcessNodeMapper.findBaseMapByIds(nodeHandlersMap.keySet(), false);

            List<ControlDataValueDTO> dataValues = new ArrayList<>();
            if (isCover) {
                // 完全覆盖
                nodeHandlersMap.forEach((key, value) -> {
                    if (!nodeMap.containsKey(key)) {
                        throw new BusinessException(RCodeEnum.APPLICATION_PROCESS_NODE_NOT_EXIST);
                    }
                    NodeBaseDTO nodeBase = nodeMap.get(key);
                    dataValues.add(new ControlDataValueDTO(nodeBase.getNodeId(), nodeBase.getNodeName()).setOtherInfo(new NodeOtherInfoDTO(value)));
                });
            } else {
                List<ControlDataValueDTO> oldDataValues = JSON.parseArray(orderData.getOrderData(), ControlDataValueDTO.class);
                if (isOnlyModifyHandlers) {
                    // 只更新负责人
                    oldDataValues.forEach(oldDataValue -> {
                        oldDataValue.setOtherInfo(new NodeOtherInfoDTO(nodeHandlersMap.getOrDefault(oldDataValue.getId(), new ArrayList<>())));
                        dataValues.add(oldDataValue);
                    });
                } else {
                    // 已有节点
                    oldDataValues.forEach(oldDataValue -> {
                        // 非父节点（上一个节点）都要保留
                        if (Objects.nonNull(parentNodeId) && !oldDataValue.getId().equals(parentNodeId)) {
                            List<UserEnterpriseBaseDTO> handlers = nodeHandlersMap.getOrDefault(oldDataValue.getId(), new ArrayList<>());
                            if (!handlers.isEmpty()) {
                                oldDataValue.setOtherInfo(new NodeOtherInfoDTO(handlers));
                            }
                            dataValues.add(oldDataValue);
                        }
                    });
                    // 更新负责人
                    Map<Long, ControlDataValueDTO> nodeValueMap = StreamUtil.toMapK(oldDataValues, ControlDataValueDTO::getId);
                    // 新节点
                    nodeHandlersMap.forEach((key, value) -> {
                        if (!nodeValueMap.containsKey(key)) {
                            if (!nodeMap.containsKey(key)) {
                                throw new BusinessException(RCodeEnum.APPLICATION_PROCESS_NODE_NOT_EXIST);
                            }
                            NodeBaseDTO nodeBase = nodeMap.get(key);
                            dataValues.add(new ControlDataValueDTO(nodeBase.getNodeId(), nodeBase.getNodeName()).setOtherInfo(new NodeOtherInfoDTO(value)));
                        }
                    });
                }
            }

            orderData.setOrderData(JSON.toJSONString(dataValues));
        });
    }

    /**
     * 订单删除 当前节点控件 数据
     *
     * @param changeOrderNodeDataReqDTO changeOrderNodeDataReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void changeOrderNodeData(ChangeOrderNodeDataReqDTO changeOrderNodeDataReqDTO) {
        Long orderId = changeOrderNodeDataReqDTO.getOrderId();
        List<Long> nodeIds = changeOrderNodeDataReqDTO.getNodeIds();
        Integer handleType = changeOrderNodeDataReqDTO.getHandleType();

        HandleTypeEnum.findById(handleType);
        ApplicationOrder order = applicationOrderMapper.findById(orderId);

        // 当前节点
        List<ApplicationProcessNode> nodes = applicationProcessNodeMapper.findByIds(nodeIds, false);
        if (nodeIds.size() != nodes.size()) {
            throw new BusinessException(RCodeEnum.APPLICATION_PROCESS_NODE_NOT_EXIST);
        }

        // 更新订单节点信息
        List<ApplicationOrderNode> orderNodes = this.baseMapper.findByOrderNodes(ListUtil.toList(changeOrderNodeDataReqDTO)).stream()
                .peek(e -> e.setHandleType(handleType))
                .collect(Collectors.toList());
        applicationLogService.saveAndCheck(this.baseMapper, ApplicationOrderNode::getOrderNodeId, orderNodes);

        // 更新订单节点控件数据
        applicationOrderDataService.modifyOrderNodeData(order, orderData -> {
            // 移除指定节点信息
            List<ControlDataValueDTO> dataValues = JSON.parseArray(orderData.getOrderData(), ControlDataValueDTO.class);
            dataValues = dataValues.stream().filter(e -> !nodeIds.contains(e.getId())).collect(Collectors.toList());

            orderData.setOrderData(JSON.toJSONString(dataValues));
        });
    }
}
